//
// Created on 2025/7/23.
//
// Node APIs are not fully supported. To solve the compilation error of the interface cannot be found,
// please include "napi/native_api.h".

#ifndef STREAMCORE_STREAM_IMPL_H
#define STREAMCORE_STREAM_IMPL_H

#include "stream_base.h"

#define DEFINE_METHOD(name, method)                                                                                    \
    { name, nullptr, method, nullptr, nullptr, nullptr, napi_default, nullptr }

#define DEFINE_GETTER(name, method)                                                                                    \
    { name, nullptr, nullptr, method, nullptr, nullptr, napi_default, nullptr }

#define DEFINE_SETTER(name, method)                                                                                    \
    { name, nullptr, nullptr, nullptr, method, nullptr, napi_default, nullptr }

#define DEFINE_ACCESSOR(name, getter, setter)                                                                          \
    { name, nullptr, nullptr, getter, setter, nullptr, napi_default, nullptr }

static napi_status my_set_prototype(napi_env env, napi_value obj, napi_value proto) {
    napi_value global, object, set_prototype_of, argv[2], result;

    // 1. 获取 Object.setPrototypeOf
    napi_get_global(env, &global);
    napi_get_named_property(env, global, "Object", &object);
    napi_get_named_property(env, object, "setPrototypeOf", &set_prototype_of);

    // 2. 准备参数：obj, proto
    argv[0] = obj;
    argv[1] = proto;

    // 3. 调用 Object.setPrototypeOf(obj, proto)
    return napi_call_function(env, object, set_prototype_of, 2, argv, &result);
}

template <typename Derived> class StreamImpl : public StreamBase<Derived> {
public:
    using Traits = typename Derived::NapiTraits;

    static void Export(napi_env env, napi_value exports) {
        napi_value constructor;
        napi_get_named_property(env, exports, Traits::ClassName, &constructor);

        napi_valuetype type;
        napi_typeof(env, constructor, &type);

        if (type != napi_undefined)
            return;

        constexpr const int property_count = Traits::PropertyCount + sizeof(PropertiesBase) / sizeof(PropertiesBase[0]);
        std::array<napi_property_descriptor, property_count> properties;
        memcpy(properties.data(), PropertiesBase, sizeof(PropertiesBase));
        memcpy(properties.data() + sizeof(PropertiesBase), Traits::Properties, sizeof(Traits::Properties));
        napi_define_class(env, Traits::ClassName, strlen(Traits::ClassName), JsConstructor, nullptr, property_count,
                          properties.data(), &constructor);
        napi_set_named_property(env, exports, Traits::ClassName, constructor);
        napi_create_reference(env, constructor, 0, &ref_);
    }

    static napi_value Create(napi_env env, Derived *derived) {
        napi_value obj;
        napi_create_object(env, &obj);

        napi_value constructor, prototype;
        napi_get_reference_value(env, ref_, &constructor);
        napi_get_prototype(env, constructor, &prototype);

        my_set_prototype(env, obj, prototype);
        return obj;
    }

private:
    static napi_value JsConstructor(napi_env env, napi_callback_info info) {
        NAPI_TRY(env, Traits::ClassName, {
            Derived *derived = new Derived(env, info);
            napi_value this_arg;
            GET_THIS(env, info, this_arg);
            napi_wrap(env, this_arg, derived, JsDispose, nullptr, nullptr);
        });
    }

    static void JsDispose(napi_env env, void *data, void *hint) { delete static_cast<Derived *>(data); }

    static constexpr napi_property_descriptor PropertiesBase[] = {
        DEFINE_GETTER("canRead", Derived::GetCanRead),
        DEFINE_GETTER("canWrite", Derived::GetCanWrite),
        DEFINE_GETTER("canSeek", Derived::GetCanSeek),
        DEFINE_GETTER("position", Derived::GetPosition),
        DEFINE_ACCESSOR("length", Derived::GetLength, Derived::SetLength),
        DEFINE_GETTER("isClosed", Derived::GetIsClosed),
        DEFINE_METHOD("read", Derived::Read),
        DEFINE_METHOD("readSync", Derived::ReadSync),
        DEFINE_METHOD("write", Derived::Write),
        DEFINE_METHOD("writeSync", Derived::WriteSync),
        DEFINE_METHOD("seek", Derived::Seek),
        DEFINE_METHOD("seekSync", Derived::SeekSync),
        DEFINE_METHOD("flush", Derived::Flush),
        DEFINE_METHOD("flushSync", Derived::FlushSync),
        DEFINE_METHOD("copyTo", Derived::CopyTo),
        DEFINE_METHOD("copyToSync", Derived::CopyToSync),
        DEFINE_METHOD("close", Derived::Close),
        DEFINE_METHOD("closeSync", Derived::CloseSync),
    };

    static napi_ref ref_;
};

#endif // STREAMCORE_STREAM_IMPL_H
